﻿using Morus;
using Morus.Infrastructure;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Linq.Expressions;

namespace KAKA.Repository
{
    public abstract class Repository<TAggregateRoot> : BaseRepository<TAggregateRoot>
        where TAggregateRoot : class, IAggregateRoot
    {
        public Repository(Database database)
        {
            Check.Argument.IsNotNull(database, "database");
            Database = database;
        }

        protected Database Database { get; private set; }

        public override void Add(TAggregateRoot aggregateRoot)
        {
            Add<TAggregateRoot>(aggregateRoot);
        }
        public void Add<Entity>(Entity entity) where Entity : class
        {
            Database.Set<Entity>().Add(entity);
        }

        protected override IQueryable<TAggregateRoot> GetAll()
        {
            return CreateQueryable<TAggregateRoot>();
        }

        protected override IQueryable<TAggregateRoot> Find(ISpecification<TAggregateRoot> specification)
        {
            return GetByFilter(specification);
        }

        protected override TAggregateRoot Get(ISpecification<TAggregateRoot> specification)
        {
            return Get<TAggregateRoot>(specification);
        }

        public override void Commit()
        {
            Database.SaveChanges();
        }

        protected virtual IQueryable<TEntity> GetByFilter<TEntity>(ISpecification<TEntity> specification) where TEntity : class
        {
            return CreateQueryable<TEntity>().Where(specification.Expression);
        }

        protected virtual TEntity Get<TEntity>(ISpecification<TEntity> specification) where TEntity : class
        {
            return GetByFilter<TEntity>(specification).FirstOrDefault();
        }

        protected virtual PagedResult<TEntity> GetPaged<TEntity, S>(ISpecification<TEntity> specification, int start, int record, Expression<Func<TEntity, S>> sorting, bool ascending) where TEntity : class
        {
            Check.Argument.IsNotNegative(start, "start");
            Check.Argument.IsNotNegative(record, "record");
            Check.Argument.IsNotNull(sorting, "sorting");

            var total = CreateQueryable<TEntity>().Where(specification.Expression).Count();

            var result = CreateQueryable<TEntity>().Where(specification.Expression);

            result = (ascending)
                                ?
                                    result.OrderBy(sorting)
                                :
                                    result.OrderByDescending(sorting);

            result = result.Skip(start)
                           .Take(record);

            return BuildPagedResult<TEntity>(result.AsEnumerable<TEntity>(), total);
        }

        protected virtual PagedResult<TEntity> GetPaged<TEntity, S>(int start, int record, Expression<Func<TEntity, S>> sorting, bool ascending) where TEntity : class
        {
            Check.Argument.IsNotNegative(start, "start");
            Check.Argument.IsNotNegative(record, "record");
            Check.Argument.IsNotNull(sorting, "sorting");

            var total = CreateQueryable<TEntity>().Count();

            var result = CreateQueryable<TEntity>();

            result = (ascending)
                                ?
                                    result.OrderBy(sorting)
                                :
                                    result.OrderByDescending(sorting);

            result = result.Skip(start)
                           .Take(record);

            return BuildPagedResult<TEntity>(result.AsEnumerable<TEntity>(), total);
        }

        protected static PagedResult<T> BuildPagedResult<T>(IEnumerable<T> source, int total)
        {
            return new PagedResult<T>(source.ToArray(), total);
        }

        protected static ICollection<T> BuildReadOnlyResult<T>(IEnumerable<T> source)
        {
            return source.ToArray();
        }

        protected virtual IQueryable<TEntity> CreateQueryable<TEntity>() where TEntity : class
        {
            return Database.Set<TEntity>();
        }

        protected virtual void Destroy<TEntity>(TEntity entity) where TEntity : class
        {
            Database.Set<TEntity>().Remove(entity);
        }
        protected virtual void Destroy(TAggregateRoot aggregateRoot)
        {
            Destroy<TAggregateRoot>(aggregateRoot);
        }

        protected virtual void TrackingAggregateRoot(TAggregateRoot aggregateRoot)
        {
            throw new NotImplementedException();
        }
    }
}
